package org.jeecg.common.rc.exception;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import org.jeecg.common.core.Result;
import org.jeecg.common.util.JlStrUtil;
import org.jeecg.common.util.RefUtil;
import org.jeecg.common.util.util_entity.core.CommCode;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLSyntaxErrorException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import kotlin.Pair;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * 异常处理器
 *
 * @Author scott
 * @Date 2019
 */
@RestControllerAdvice
@Slf4j
public class JlExceptionHandler {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Value("${spring.servlet.multipart.max-file-size}")
    private String maxFileSize;

    public static Pair<String, String> extract(String input) {
        String unknownColumn = null;
        String mapperFile = null;
        String[] lines = input.split("\n");
        for (String line : lines) {
            if (line.contains("Unknown column")) {
                int startIndex = line.indexOf("'");
                int endIndex = line.indexOf("'", startIndex + 1);
                unknownColumn = line.substring(startIndex + 1, endIndex);
            } else if (line.contains(".java")) {
                int startIndex = line.indexOf("in ");
                int endIndex = line.indexOf(".java");
                mapperFile = line.substring(startIndex + 3, endIndex + 5);
            }
        }
        Pair<String, String> objectObjectPair = new Pair<>(unknownColumn, mapperFile);

        return objectObjectPair;
    }

    @ExceptionHandler(SQLSyntaxErrorException.class)
    public Result<?> handleJltException(SQLSyntaxErrorException e) {
        log.error(e.getMessage(), e);
        return Result.error(e.getMessage());
    }

    /**
     * 自己修复unknowColumn
     *
     * @param e e 入参
     * @return {@link Result }<{@link ? }>
     * @author zjarlin
     * @since 2023/04/08
     */
    @SneakyThrows
    @ExceptionHandler(BadSqlGrammarException.class)
    @Profile("dev")
    public Result<?> handleJltException(BadSqlGrammarException e) {
        String message = e.getMessage();
        String unknownColumn = CharSequenceUtil.subBetween(message, "Unknown column '", "'");
        unknownColumn = StrUtil.toUnderlineCase(unknownColumn);
        String mapperFile = CharSequenceUtil.subBetween(message, "The error may exist in ", ".java") + ".java";
        String tabName = CharSequenceUtil.subBetween(message, "FROM ", " WHERE");
        if (StrUtil.equals(mapperFile, "null.java")) {
            // 使用正则表达式匹配 Mapper 文件名
            String mapperName = ReUtil.get("mapper\\.([^.\\s]+)", message, 1);
            if (CharSequenceUtil.isBlank(mapperName)) {
//                MethodAnnotationScanner.DIRECTLY.getAnnotationsIfSupport()
                Set<Class<?>> classes = ClassScanner.scanAllPackageByAnnotation("org.jeecg.modules", TableName.class);
                Class<?> className = classes.stream().filter(x -> {
                            TableName annotation = x.getAnnotation(TableName.class);
                            return StrUtil.equals(annotation.value(), CharSequenceUtil.subBetween(message, "FROM ", " WHERE"));
                        })
                        .findAny()
                        .map(x -> {
                            String name = x.getName();
                            String simpleName = x.getSimpleName();
//                            //获取上上级crud目录
                            String pathFromRight = JlStrUtil.getPathFromRight(name, 2);
                            String join = StrUtil.join(".", pathFromRight, "mapper", simpleName + "Mapper");
                            Class<?> clazz = ClassUtil.loadClass(join);
                            return clazz;
//                            //获取controller引用
//                            String controllerRef = StrUtil.join(".", pathFromRight, "controller", controllerName);
//                            String serviceRef = StrUtil.join(".", pathFromRight, "service", serviceName);
//                            String iServiceRef = StrUtil.join(".", pathFromRight, "service", iServiceName);
                        })
                        .orElse(null);
//               mapperName= Objects.isNull(className)?null: className.getSimpleName();
                return getObjectResult(e, unknownColumn, className);

            }
            System.out.println("Mapper 文件名为：" + mapperName);

//             使用正则表达式匹配 Mapper 所在的包名
            String packageName = ReUtil.get("(\\S+)\\." + mapperName, message, 1);
            System.out.println("Mapper 所在的包名为：" + packageName);

            // 拼接 Mapper 类的全限定类名
            String className = mapperName;
            String mapperClassName = String.format("%s.%s", packageName, className);

            Class<?> mapperClass = Class.forName(mapperClassName);
            return getObjectResult(e, unknownColumn, mapperClass);
        }

        String replace = mapperFile.replace("/", ".").replace(".java", "");
        Class<?> mapperClass = Class.forName(replace);
        return getObjectResult(e, unknownColumn, mapperClass);
//		Class<?> entityClass = (Class<?>) ((ParameterizedType) mapperClass.getGenericInterfaces()[0]).getActualTypeArguments()[0];
//		List<String> addDMLs = RefUtil.getAddDMLs(entityClass);
//		List<String> dmlFixs = addDMLs.stream().filter(s -> CharSequenceUtil.contains(s, unknownColumn)).collect(Collectors.toList());
//		if (CollUtil.isNotEmpty(dmlFixs)) {
//			dmlFixs.forEach(jdbcTemplate::execute);
//		}
//		log.error(e.getMessage(), e);
//		return Result.error(e.getMessage());
    }

    /**
     * 处理valid异常信息
     * @param e 入参
     * @return {@link Result }<{@link ? }>
     * @author zjarlin
     * @since 2023/12/01
     */

//    @ExceptionHandler(MethodArgumentNotValidException.class)
//    public Result<?> handleException(MethodArgumentNotValidException e) {
//        BindingResult bindingResult = e.getBindingResult();
//        FieldError fieldError = bindingResult.getFieldError();
//        if (Objects.isNull(fieldError)) {
//            String message = e.getMessage();
//            return Result.error(message);
//
//        }
//        String defaultMessage = fieldError.getDefaultMessage();
//        return Result.error(defaultMessage);
//    }

    private Result<Object> getObjectResult(BadSqlGrammarException e, String unknownColumn, Class<?> mapperClass) {
        Class<?> entityClass = (Class<?>) ((ParameterizedType) mapperClass.getGenericInterfaces()[0]).getActualTypeArguments()[0];
        List<String> addDMLs = RefUtil.getAddDMLs(entityClass);
        List<String> dmlFixs = addDMLs.stream().filter(s -> CharSequenceUtil.contains(s, unknownColumn)).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(dmlFixs)) {
            dmlFixs.forEach(jdbcTemplate::execute);
        }
        log.error(e.getMessage(), e);
        return Result.error(e.getMessage());
    }

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(JlException.class)
    public Result<?> handleJltException(JlException e) {
        log.error(e.getMessage(), e);
        return Result.error(e.getMessage());
    }

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(Jl401Exception.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result<?> handleJeecgBoot401Exception(Jl401Exception e) {
        log.error(e.getMessage(), e);
        return new Result(401, e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<?> handlerNoFoundException(Exception e) {
        log.error(e.getMessage(), e);
        return Result.error(404, "路径不存在，请检查路径是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public Result<?> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error(e.getMessage(), e);
        return Result.error("数据库中已存在该记录");
    }

    //@ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
    //public Result<?> handleAuthorizationException(AuthorizationException e){
    //	log.error(e.getMessage(), e);
    //	return Result.noauth("没有权限，请联系管理员授权");
    //}

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleValidateException(MethodArgumentNotValidException e) {
        log.warn("校验异常", e);
        // 不合格的字段，可能有多个，只需要返回其中一个提示用户就行
        // 比如密码为空
        List<FieldError> fieldErrors = e.getFieldErrors();
        String s = ";" + System.lineSeparator();

        Integer code = CommCode.PARAM_IS_INVALID.getCode();

        String defaultMessage = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(s));
        return Result.error(code, defaultMessage);

    }

    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        log.error(e.getMessage(), e);
        return Result.error("操作失败，" + e.getMessage());
    }
    //@ExceptionHandler(HttpHostConnectException.class)
    //public Result<?> handleJltException(HttpHostConnectException e){
    //	log.error(e.getMessage(), e);
    //	return Result.error("无法连接目标服务器"+e.getMessage());
    //}

    /**
     * @param e
     * @return
     * @Author 政辉
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<?> HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        StringBuffer sb = new StringBuffer();
        sb.append("不支持");
        sb.append(e.getMethod());
        sb.append("请求方法，");
        sb.append("支持以下");
        String[] methods = e.getSupportedMethods();
        if (methods != null) {
            for (String str : methods) {
                sb.append(str);
                sb.append("、");
            }
        }
        log.error(sb.toString(), e);
        //return Result.error("没有权限，请联系管理员授权");
        return Result.error(405, sb.toString());
    }

    /**
     * spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.error(e.getMessage(), e);
        return Result.error(String.format("文件大小超出%s限制, 请压缩或降低文件质量! ", maxFileSize));
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
        String message = e.getMessage();
        log.error(message, e);

        log.error(message, e);
        if (StrUtil.containsIgnoreCase(message,"Data too long")) {
            //todo 修复字段过长
            String s = StrUtil.subBetween(message, "### SQL:", "### Cause:");
            log.info("The badSql Is:::{}",s);
//			jdbcTemplate
        }
        return Result.error("执行数据库异常,违反了完整性例如：违反惟一约束、违反非空限制、字段内容超出长度等");
    }

    //@ExceptionHandler(PoolException.class)
    //public Result<?> handlePoolException(PoolException e) {
    //	log.error(e.getMessage(), e);
    //    return Result.error("Redis 连接异常!");
    //}

}
